package tr.edu.metu.is.mega.client.widgets;

import java.util.Date;
import java.util.Map;

import tr.edu.metu.is.mega.client.service.GwtValidationService;
import tr.edu.metu.is.mega.client.service.GwtValidationServiceAsync;
import tr.edu.metu.is.mega.common.ValidationAttributeValue;
import tr.edu.metu.is.mega.common.ValidationConstraint;

import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.Container;
import com.extjs.gxt.ui.client.widget.MessageBox;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * Decorator to set validation constraints for components.
 */
public class ConstraintDecorator {

    /**
     * Decorates components with validation constraints.
     * 
     * @param dtoClass dto class
     * @param container container to decore
     */
    public void decorateComponents(String dtoClass, final Container<Component> container) {
        GwtValidationServiceAsync validationService = GwtValidationService.Util.getInstance();
        AsyncCallback<Map<String, Map<ValidationConstraint, ValidationAttributeValue>>> callback = null;
        callback = new AsyncCallback<Map<String, Map<ValidationConstraint, ValidationAttributeValue>>>() {

            @Override
            public void onFailure(Throwable caught) {
                MessageBox.alert("Hata", "Doğrulama parametreleri ayarlanamıyor", null);
            }

            @Override
            public void onSuccess(Map<String, Map<ValidationConstraint, ValidationAttributeValue>> result) {
                setConstraints(result, container);
            }
        };
        validationService.getValidationConstraints(dtoClass, callback);
    }

    /**
     * Sets constraints to components.
     * 
     * @param constraintMap map of constraints
     * @param container container to decorate
     */
    @SuppressWarnings("all")
    private void setConstraints(Map<String, Map<ValidationConstraint, ValidationAttributeValue>> constraintMap, Container<Component> container) {
        for (int i = 0; i < container.getItemCount(); ++i) {
            Component o = container.getItem(i);
            if (o instanceof Container<?>) {
                setConstraints(constraintMap, (Container<Component>) o);
            } else if (o instanceof MegaField) {
                setConstraint(constraintMap, o);
            }
        }
    }

    /**
     * Sets constraints to components.
     * 
     * @param constraintMap map of constraints
     * @param f field to decorate
     */
    private void setConstraint(Map<String, Map<ValidationConstraint, ValidationAttributeValue>> constraintMap, Component f) {
        if (f instanceof MegaTextField) {
            MegaTextField field = (MegaTextField) f;
            Map<ValidationConstraint, ValidationAttributeValue> map = constraintMap.get(field.getBoundAttribute());
            if (map != null && map.size() > 0) {
                decorateTextField(field, map);
            }
        } else if (f instanceof MegaTextArea) {
            MegaTextArea field = (MegaTextArea) f;
            Map<ValidationConstraint, ValidationAttributeValue> map = constraintMap.get(field.getBoundAttribute());
            if (map != null && map.size() > 0) {
                decorateTextArea(field, map);
            }
        } else if (f instanceof MegaNumberField) {
            MegaNumberField field = (MegaNumberField) f;
            Map<ValidationConstraint, ValidationAttributeValue> map = constraintMap.get(field.getBoundAttribute());
            if (map != null && map.size() > 0) {
                decorateNumberField(field, map);
            }
        } else if (f instanceof MegaDateField) {
            MegaDateField field = (MegaDateField) f;
            Map<ValidationConstraint, ValidationAttributeValue> map = constraintMap.get(field.getBoundAttribute());
            if (map != null && map.size() > 0) {
                decorateDateField(field, map);
            }
        } else if (f instanceof MegaTimeField) {
            MegaTimeField field = (MegaTimeField) f;
            Map<ValidationConstraint, ValidationAttributeValue> map = constraintMap.get(field.getBoundAttribute());
            if (map != null && map.size() > 0) {
                decorateTimeField(field, map);
            }
        } else if (f instanceof MegaComboBox) {
            MegaComboBox<?> field = (MegaComboBox<?>) f;
            Map<ValidationConstraint, ValidationAttributeValue> map = constraintMap.get(field.getBoundAttribute());
            if (map != null && map.size() > 0) {
                decorateComboField(field, map);
            }
        } else if (f instanceof MegaSlider) {
            MegaSlider field = (MegaSlider) f;
            Map<ValidationConstraint, ValidationAttributeValue> map = constraintMap.get(field.getBoundAttribute());
            if (map != null && map.size() > 0) {
                decorateSliderField(field, map);
            }
        } else if (f instanceof MegaSpinnerField) {
            MegaSpinnerField field = (MegaSpinnerField) f;
            Map<ValidationConstraint, ValidationAttributeValue> map = constraintMap.get(field.getBoundAttribute());
            if (map != null && map.size() > 0) {
                decorateSpinnerField(field, map);
            }
        }
    }

    /**
     * Decorates a slider with constraints.
     * 
     * @param field field to decorate
     * @param map constraint map
     */
    private void decorateSliderField(MegaSlider field, Map<ValidationConstraint, ValidationAttributeValue> map) {
        ValidationAttributeValue val = map.get(ValidationConstraint.MAX_VALUE);
        if (val != null) {
            field.setMaxValue(val.getIntValue());
        }
        val = map.get(ValidationConstraint.MIN_VALUE);
        if (val != null) {
            field.setMinValue(val.getIntValue());
        }
    }

    /**
     * Decorates a combo box.
     * 
     * @param field combo box
     * @param map map of constraints
     */
    private void decorateComboField(MegaComboBox<?> field, Map<ValidationConstraint, ValidationAttributeValue> map) {
        ValidationAttributeValue val = map.get(ValidationConstraint.NOT_NULL);
        if (val != null && val.isBooleanValue()) {
            field.setAllowBlank(false);
            field.setFieldLabel(field.getFieldLabel() + " <font color='#FF0000'>*</font>");
        }
    }

    /**
     * Decorates date field.
     * 
     * @param field date field
     * @param map map of constraints
     */
    private void decorateDateField(MegaDateField field, Map<ValidationConstraint, ValidationAttributeValue> map) {
        ValidationAttributeValue val = map.get(ValidationConstraint.NOT_NULL);
        if (val != null && val.isBooleanValue()) {
            field.setAllowBlank(false);
            field.setFieldLabel(field.getFieldLabel() + " <font color='#FF0000'>*</font>");
        }
        Date now = new Date();
        val = map.get(ValidationConstraint.PAST_DATE);
        if (val != null && val.isBooleanValue()) {
            field.setMaxValue(now);
        }
        val = map.get(ValidationConstraint.FUTURE_DATE);
        if (val != null && val.isBooleanValue()) {
            field.setMinValue(now);
        }
    }

    /**
     * Decorates time field.
     * 
     * @param field time field to decorate
     * @param map map of constraints
     */
    private void decorateTimeField(MegaTimeField field, Map<ValidationConstraint, ValidationAttributeValue> map) {
        ValidationAttributeValue val = map.get(ValidationConstraint.NOT_NULL);
        if (val != null && val.isBooleanValue()) {
            field.setAllowBlank(false);
            field.setFieldLabel(field.getFieldLabel() + " <font color='#FF0000'>*</font>");
        }
        Date now = new Date();
        val = map.get(ValidationConstraint.PAST_DATE);
        if (val != null && val.isBooleanValue()) {
            field.setMaxValue(now);
        }
        val = map.get(ValidationConstraint.FUTURE_DATE);
        if (val != null && val.isBooleanValue()) {
            field.setMinValue(now);
        }
    }

    /**
     * Decorates spinner field.
     * 
     * @param field field instance
     * @param map map of constraints
     */
    private void decorateSpinnerField(MegaSpinnerField field, Map<ValidationConstraint, ValidationAttributeValue> map) {
        ValidationAttributeValue val = map.get(ValidationConstraint.NOT_NULL);
        if (val != null && val.isBooleanValue()) {
            field.setAllowBlank(false);
            field.setFieldLabel(field.getFieldLabel() + " <font color='#FF0000'>*</font>");
        }
        val = map.get(ValidationConstraint.MAX_LENGTH);
        if (val != null) {
            field.setMaxLength(val.getIntValue());
        }
        val = map.get(ValidationConstraint.MIN_LENGTH);
        if (val != null) {
            field.setMinLength(val.getIntValue());
        }
        val = map.get(ValidationConstraint.MAX_VALUE);
        if (val != null) {
            field.setMaxValue(val.getIntValue());
        }
        val = map.get(ValidationConstraint.MIN_VALUE);
        if (val != null) {
            field.setMinValue(val.getIntValue());
        }
    }

    /**
     * Decorates number field.
     * 
     * @param field field instance
     * @param map map of constraints
     */
    private void decorateNumberField(MegaNumberField field, Map<ValidationConstraint, ValidationAttributeValue> map) {
        ValidationAttributeValue val = map.get(ValidationConstraint.NOT_NULL);
        if (val != null && val.isBooleanValue()) {
            field.setAllowBlank(false);
            field.setFieldLabel(field.getFieldLabel() + " <font color='#FF0000'>*</font>");
        }
        val = map.get(ValidationConstraint.MAX_LENGTH);
        if (val != null) {
            field.setMaxLength(val.getIntValue());
        }
        val = map.get(ValidationConstraint.MIN_LENGTH);
        if (val != null) {
            field.setMinLength(val.getIntValue());
        }
        val = map.get(ValidationConstraint.MAX_VALUE);
        if (val != null) {
            field.setMaxValue(val.getIntValue());
        }
        val = map.get(ValidationConstraint.MIN_VALUE);
        if (val != null) {
            field.setMinValue(val.getIntValue());
        }
    }

    /**
     * Decorates a MskTextArea with constraints.
     * 
     * @param field field instance
     * @param map map of constraints
     */
    private void decorateTextArea(MegaTextArea field, Map<ValidationConstraint, ValidationAttributeValue> map) {
        ValidationAttributeValue val = map.get(ValidationConstraint.NOT_NULL);
        if (val != null && val.isBooleanValue()) {
            field.setAllowBlank(false);
            field.setFieldLabel(field.getFieldLabel() + " <font color='#FF0000'>*</font>");
        }
        val = map.get(ValidationConstraint.MAX_LENGTH);
        if (val != null) {
            field.setMaxLength(val.getIntValue());
        }
        val = map.get(ValidationConstraint.MIN_LENGTH);
        if (val != null) {
            field.setMinLength(val.getIntValue());
        }
    }

    /**
     * Decorates MskTextfield constraints.
     * 
     * @param field field instance
     * @param map map of constraints
     */
    private void decorateTextField(MegaTextField field, Map<ValidationConstraint, ValidationAttributeValue> map) {
        ValidationAttributeValue val = map.get(ValidationConstraint.NOT_NULL);
        if (val != null && val.isBooleanValue()) {
            field.setAllowBlank(false);
            field.setFieldLabel(field.getFieldLabel() + " <font color='#FF0000'>*</font>");
        }
        val = map.get(ValidationConstraint.MAX_LENGTH);
        if (val != null) {
            field.setMaxLength(val.getIntValue());
        }
        val = map.get(ValidationConstraint.MIN_LENGTH);
        if (val != null) {
            field.setMinLength(val.getIntValue());
        }
    }
}
